// axiom: F-F-F-F-F-F-F-F
// rules: 
//  F => -F++F-

// depth: 5
// stepsPerFrame: 10
// angle: 45

// actions:
//   F => draw()

import { _sin, _cos, _polar, _rotate, _random } from "../math/index.js";

const _iter = (axiom = '', rules = {}) => {
    let tmp, result = ''
    axiom.split('').forEach(t => {
        tmp = rules[t];
        if (tmp) {
            result += tmp;
        } else {
            result += t;
        }
    });
    return result
}

const _grammar = (options) => {
    let { axiom = 'F', rules = { F: 'F[+F]' }, depth = 5, angle = 60 } = options
    let result = axiom;
    for (let i = 0; i < depth; i++) {
        result = _iter(result, rules);
    }
    return result
}


export const lSystemPoints = (options) => {
    let { o = [0, 0], r = 20, angle = 60, wriggle = 10, reduction = 1.0 } = options
    let _ra = () => {
        return wriggle ? _random(-wriggle, wriggle) : 0
    }
    let a = 0
    let stack = []
    let point = o
    let radius = r
    let result = [[point]]
    let _branch = () => {
        result[result.length] = [point]
    }
    let _grow = () => {
        result[result.length - 1].push([...point])
    }
    let _forword = () => {
        point = _polar(point, radius, a - 90)
    }
    let _turnLeft = () => {
        a -= angle + _ra();
    }
    let _turnRight = () => {
        a += angle + _ra();
    }
    let _move = () => {
        _forword()
        _grow()
    }
    let _move2 = () => {
        _forword()
        _branch()
    }
    let _push = () => {
        stack.push({ point: [...point], a: a });
        radius *= reduction
    }
    let _pop = () => {
        ({ point: point, a: a } = stack.pop());
        _branch()
        radius /= reduction
    }
    let actions = {
        'F': _move,
        'f': _move2,
        '+': _turnLeft,
        '-': _turnRight,
        '[': _push,
        ']': _pop
    }
    let grammer = _grammar(options)

    grammer = grammer.replace(/[^F+-\[\]]/g, '').split('');

    grammer.forEach(t => {
        var act = actions[t];
        if (act) {
            act();
        }
    });

    return result
}

